package org.jgear.util;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

public class TimeDiff {
	private long diff;
	private Locale locale;

	private static final String YEAR = "year";
	private static final String MONTH = "month";
	private static final String WEEK = "week";
	private static final String DAY = "day";
	private static final String HOUR = "hour";
	private static final String MINUTE = "minute";
	private static final String SECOND = "second";

	private static final SimpleDateFormat sdf = new SimpleDateFormat(
			"yyyyMMddHHmmss");
	private static final String RESOURCE_BUNDLE_BASE = "org.jgear.util.timediff";
	private static ResourceBundle defaultResourceBundle = ResourceBundle
			.getBundle(RESOURCE_BUNDLE_BASE);
	private ResourceBundle currentResourceBundle = null;

	private TimeDiff() {
	}

	public static TimeDiff diff(String time) throws Exception {
		time = time.length() == 8 ? time + "000000" : time;
		return diff(sdf.parse(time).getTime(), System.currentTimeMillis());
	}

	public static TimeDiff diff(Date time) {
		return diff(time.getTime(), System.currentTimeMillis());
	}

	public static TimeDiff diff(int time) {
		return diff(time * 1000L, System.currentTimeMillis());
	}

	public static TimeDiff diff(long time) {
		return diff(time, System.currentTimeMillis());
	}

	public static void setDefaultLocale(Locale locale) {
		defaultResourceBundle = ResourceBundle.getBundle(RESOURCE_BUNDLE_BASE,
				locale);
	}

	public static TimeDiff diff(String time, String time2) throws Exception {
		time = time.length() == 8 ? time + "000000" : time;
		time2 = time2.length() == 8 ? time2 + "000000" : time2;
		return diff(sdf.parse(time).getTime(), sdf.parse(time2).getTime());
	}

	public static TimeDiff diff(Date time, Date time2) {
		return diff(time.getTime(), time2.getTime());
	}

	public static TimeDiff diff(int time, int time2) {
		return diff(time * 1000L, time2 * 1000L);
	}

	public static TimeDiff diff(long time, long time2) {
		TimeDiff timeDiff = new TimeDiff();
		timeDiff.setDiff(time2 - time);
		return timeDiff;
	}

	public static String diffText(String time) throws Exception {
		return diff(time).toString();
	}

	public static String diffText(String time, String time2) throws Exception {
		return diff(time, time2).toString();
	}

	public static String diffText(Date time) {
		return diff(time).toString();
	}

	public static String diffText(Date time, Date time2) {
		return diff(time, time2).toString();
	}
	
	public static String diffText(int time) {
		return diff(time).toString();
	}

	public static String diffText(int time, int time2) {
		return diff(time, time2).toString();
	}
	
	public static String diffText(long time) {
		return diff(time).toString();
	}
	
	public static String diffText(long time, long time2) {
		return diff(time, time2).toString();
	}

	public String toString() {
		return toString(2);
	}

	public String toString(int depth) {
		Map map = new LinkedHashMap();
		int abs = (int) Math.abs(getDiff() / 1000);
		if (map.size() < depth) {
			abs = add(map, YEAR, abs, 31536000);
		}
		if (map.size() < depth) {
			abs = add(map, WEEK, abs, 604800);
		}
		if (map.size() < depth) {
			abs = add(map, DAY, abs, 86400);
		}
		if (map.size() < depth) {
			abs = add(map, HOUR, abs, 3600);
		}
		if (map.size() < depth) {
			abs = add(map, MINUTE, abs, 60);
		}
		if (map.size() < depth) {
			add(map, SECOND, abs, 1);
		}
		return toString(map);
	}

	private int add(Map map, String key, int value, int unit) {
		if (value < unit) {
			return value;
		}
		int mod = value % unit;
		value = value / unit;
		if (value > 0) {
			map.put(checkPluralAndMakeKey(key, value), new Integer(value));
		}
		return mod;
	}

	private String checkPluralAndMakeKey(String key, int no) {
		return "timediff." + (no == 1 ? key : key + "s");
	}

	public String toString(Map map) {
		if (currentResourceBundle == null) {
			currentResourceBundle = defaultResourceBundle;
		}
		StringBuffer buffer = new StringBuffer();
		Iterator iter = map.keySet().iterator();
		while (iter.hasNext()) {
			String key = (String) iter.next();
			Integer value = (Integer) map.get(key);
			buffer.append(value).append(" ");
			buffer.append(currentResourceBundle.getString(key)).append(" ");
		}
		buffer.append(getDiff() < 0 ? currentResourceBundle
				.getString("timediff.after") : currentResourceBundle
				.getString("timediff.ago"));
		return buffer.toString();
	}

	//
	// getters and setters
	//
	public long getDiff() {
		return diff;
	}

	public void setDiff(long diff) {
		this.diff = diff;
	}

	public Locale getLocale() {
		return locale;
	}

	public TimeDiff setLocale(Locale locale) {
		this.locale = locale;
		currentResourceBundle = ResourceBundle.getBundle(RESOURCE_BUNDLE_BASE,
				locale);
		return this;
	}
}
